<?php


namespace Modules\Common\General\Security\Authorization\Concerns;


use Illuminate\Support\Facades\DB;
use Modules\Common\General\Security\Authorization\Casbin;
use Modules\Common\Models\AdminRole;

trait ManageRole
{
    /**
     * 如果一个角色没有被继承，getAllRoles不会返回该角色，这会导致给角色添加权限后查询不到角色，故添加一个虚拟的角色
     * @param  int  $roleId
     * @return mixed
     */
    public function addRole(int $roleId)
    {
        return Casbin::addRoleForUser($this->virtualSubject, "r$roleId");
    }

    public function deleteRole(int $roleId)
    {
        return Casbin::deleteRole("r$roleId");
    }

    public function deleteRoleClearly(int $roleId)
    {
        DB::transaction(function () use ($roleId) {
            $this->deleteRole($roleId);
            Casbin::deleteRolesForUser("r$roleId");
        });
    }

    public function getAllRoles()
    {
        return Casbin::getAllRoles();
    }

    public function getAllRolesHierarchy()
    {
        foreach ($this->getAllRoles() as $role) {
            $arr[$role] = $this->getRoleHierarchyByString($role);
        }
        return $arr;
    }

    public function getAllRolesHierarchyWithPermission()
    {
        foreach ($this->getAllRoles() as $role) {
            $arr[$role] = $this->getRoleHierarchyWithPermissionsByString($role);
        }
        return $arr;
    }


    public function extendsRole($roleId, $baseRoleId)
    {
        if ($roleId == $baseRoleId) {
            return false;
        }
        DB::transaction(function () use ($roleId, $baseRoleId) {
            if (in_array("r$roleId", Casbin::getRolesForUser("r$baseRoleId"))) {
                throw new \Exception('父角色不能继承子角色');
            }
            Casbin::addRoleForUser("r$roleId", "r$baseRoleId");
        });
        return true;
    }

    public function cancelExtendsRole($roleId, $baseRoleId)
    {
        DB::transaction(function () use ($roleId, $baseRoleId) {
            Casbin::deleteRoleForUser("r$roleId", "r$baseRoleId");
        });
        return true;
    }


    public function getRolesForRole(int $roleId)
    {
        return Casbin::getRolesForUser("r$roleId");
    }

    public function getRolesForRoleForPresentation(int $roleId)
    {
        return $this->rolesForPresentation(Casbin::getRolesForUser("r$roleId"));
    }

    public function getImplicitRolesForRole(int $roleId)
    {
        return Casbin::getImplicitRolesForUser("r$roleId");
    }

    public function getImplicitRolesForRoleForPresentation(int $roleId)
    {
        return $this->rolesForPresentation(Casbin::getImplicitRolesForUser("r$roleId"));
    }

    public function getRoleHierarchyForRoleWithPermission(int $roleId)
    {
        return $this->getRoleHierarchyWithPermissionsByString("r$roleId");
    }

    public function getRoleHierarchyForUserWithPermission(int $userId)
    {
        return $this->getRoleHierarchyWithPermissionsByString("u$userId");
    }

    public function getRoleHierarchyForRole(int $roleId)
    {
        return $this->getRoleHierarchyByString("r$roleId");
    }

    public function getRoleHierarchyForUser(int $userId)
    {
        return $this->getRoleHierarchyByString("u$userId");
    }

    private function getRoleHierarchyByString(string $casbinIdentity)
    {
        $arr = [];
        if ($collects = Casbin::getRolesForUser($casbinIdentity)) {
            foreach ($collects as $item) {
                $arr['roles'][$item] = $this->getRoleHierarchyByString($item);
            }
        } else {
            $arr['roles'] = [];
        }
        return $arr;
    }

    private function getRoleHierarchyWithPermissionsByString(string $casbinIdentity)
    {
        $arr = [];
        if ($collects = Casbin::getRolesForUser($casbinIdentity)) {
            foreach ($collects as $item) {
                $arr['permissions'] = $this->permissionsForPresentation(Casbin::getPermissionsForUser($casbinIdentity));

                $rolePresentation = $this->rolesForPresentation([$item]);
                $arr['roles'][] = array_merge(
                    array_shift($rolePresentation),
                    $this->getRoleHierarchyWithPermissionsByString($item)
                );
            }
        } else {
            $arr['permissions'] = $this->permissionsForPresentation(Casbin::getPermissionsForUser($casbinIdentity));
            $arr['roles'] = [];
        }
        return $arr;
    }

    protected function rolesForPresentation(array $roles)
    {
        $arr = [];
        foreach ($roles as $role) {
            $roleId = substr($role, 1);
            $role = AdminRole::whereKey($roleId)->first(['id', 'name', 'created_at', 'sort']);
            $arr[] = [
                'role_id' => $role->id, 'role_name' => $role->name, 'created_at' => $role->created_at,
                'sort' => $role->sort
            ];
        }
        return $arr;
    }


}
